/*
 * Copyright (c) 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.api.data.contacts.v3;

import com.google.api.client.googleapis.GoogleUrl;
import com.google.api.client.util.DateTime;
import com.google.api.data.AbstractAtomService;
import com.google.api.data.contacts.v3.ContactsUrl.OrderBy;
import com.google.api.data.contacts.v3.ContactsUrl.SortOrder;
import com.google.api.data.contacts.v3.model.Contact;
import com.google.api.data.contacts.v3.model.ContactGroup;
import com.google.api.data.contacts.v3.model.ContactGroupsList;

import java.io.IOException;

/**
 * Service for managing Contact Groups.
 * 
 * @since 2.2
 * @author Nicolas Garnier
 */
public class ContactGroupsService extends AbstractAtomService {

  // ============= Constructors ================

  /**
   * Constructs a Google Contacts API service.
   * 
   * @param applicationName The application Name
   */
  public ContactGroupsService(String applicationName) {
    super(applicationName, ContactsApiInfo.NAMESPACE_DICTIONARY);
  }

  // =============== Setting up Service Information ================

  @Override
  protected String getDefaultApiVersion() {
    return ContactsApiInfo.VERSION;
  }

  @Override
  protected String getClientLoginServiceCode() {
    return ContactsApiInfo.AUTH_TOKEN_TYPE;
  }

  @Override
  protected String getServiceAuthenticationScope() {
    return ContactsApiInfo.AUTH_SCOPE;
  }

  // ============= API Requests Methods ================

  /**
   * Returns the list of Contact Groups available at the given Feed URL.
   * 
   * @param url The URL of the Contact Groups Feed
   * @return The List of Contact Groups available at the given Feed URL
   * @throws IOException
   */
  public ContactGroupsList getContactGroups(GoogleUrl url) throws IOException {
    return super.executeGet(url, null, false, ContactGroupsList.class);
  }

  /**
   * Returns the list of Contact Groups of the authenticated account.
   * 
   * @return The List of Contact Groups of the authenticated account
   * @throws IOException
   */
  public ContactGroupsList getContactGroups() throws IOException {
    return super.executeGet(UrlFactory.getDefaultContactGroupsFeedUrl(), null, false,
        ContactGroupsList.class);
  }

  /**
   * Returns the list of Contact Groups of the given account.
   * 
   * @param account The account of which to get the Contact Groups
   * @return The List of Contact Groups of the given account
   * @throws IOException
   */
  public ContactGroupsList getContactGroups(String account) throws IOException {
    return super.executeGet(UrlFactory.getContactGroupsFeedUrl(account), null, false,
        ContactGroupsList.class);
  }

  /**
   * Returns the list of Contact groups of the given account.
   * 
   * @param email The email of which to get the Contact Groups.
   * @param maxResults The maximum number of entries to return. If you want to
   *          receive all of the contacts, rather than only the default maximum,
   *          you can specify a very large number for max-results.
   * @param startIndex The 1-based index of the first result to be retrieved
   *          (for paging).
   * @param updatedMin The inclusive lower bound on entry update dates.
   * @param orderBy Sorting criterion.
   * @param sortOrder Sorting order direction.
   * @param showDeleted Include deleted contacts in the returned contacts feed.
   *          Deleted contacts are shown as entries that contain nothing but an
   *          <atom:id> element and a <gd:deleted> element. (Google usually
   *          retains placeholders for deleted contacts for 30 days after
   *          deletion; during that time, you can request the placeholders using
   *          the showdeleted query parameter.) Valid values are true or false.
   *          When the server decides it cannot guarantee that it still has
   *          information about all deleted contacts pertinent to the query,
   *          then it's behavior depends on the value of the requirealldeleted
   *          query parameter.
   * @param requireAllDeleted Only relevant if showdeleted and updated-min are
   *          also provided. It dictates the behavior of the server in case it
   *          detects that placeholders of some entries deleted since the point
   *          in time specified as updated-min may have been lost. If
   *          requirealldeleted is false, the server simply returns all the
   *          placeholders it still knows about. If true, the server returns the
   *          410 HTTP response code. The default value is false.
   * @return The List of Contact Groups matching the criteria.
   * @throws IOException
   */
  public ContactGroupsList getContactGroups(String email, Integer maxResults, Integer startIndex,
      DateTime updatedMin, OrderBy orderBy, SortOrder sortOrder, Boolean showDeleted,
      Boolean requireAllDeleted) throws IOException {
    ContactsUrl contactsUrl = UrlFactory.getContactGroupsFeedUrl(email);
    contactsUrl.maxResults = maxResults;
    contactsUrl.startIndex = startIndex;
    contactsUrl.updatedMin = updatedMin;
    contactsUrl.orderBy = orderBy;
    contactsUrl.sortOrder = sortOrder;
    contactsUrl.showDeleted = showDeleted;
    contactsUrl.requireAllDeleted = requireAllDeleted;
    return super.executeGet(contactsUrl, null, false, ContactGroupsList.class);
  }

  /**
   * Returns the updated version of {@code contactGroup} from the server or
   * simply {@code contactGroup} if it is already up to date.
   * 
   * @param contactGroup The contact group to check the updated version of
   * @return The update version of the given contact group if different or
   *         returns the {@code contactsEntry} if it is up to date.
   * @throws IOException
   */
  public ContactGroup getUpdatedContactGroup(ContactGroup contactGroup) throws IOException {
    GoogleUrl url = new GoogleUrl(contactGroup.getSelfLink());
    ContactGroup newContactsEntry = super.executeGet(url, contactGroup.etag, false,
        ContactGroup.class);
    if (newContactsEntry == null) {
      return contactGroup;
    } else {
      return newContactsEntry;
    }
  }

  /**
   * Returns the Contact Group available at the given URL. This can be a Self
   * Link of a Contact Group.
   * 
   * @param url The URL of the Contact Group
   * @return The Contact Group available at the given URL
   * @throws IOException
   */
  public ContactGroup getContactGroup(String url) throws IOException {
    return super.executeGet(new GoogleUrl(url), null, false, ContactGroup.class);
  }

  // ========================== Insert / POST ========================

  /**
   * Insert the given Contact Group to the given account's Contact Group list.
   * 
   * @param contactGroup the contact group to insert
   * @param account The contacts account to insert to
   * @return The inserted Contacts Group
   * @throws IOException
   */
  public ContactGroup saveNewContactGroup(ContactGroup contactGroup, String account)
      throws IOException {
    return super.executeInsert(contactGroup, UrlFactory.getContactGroupsFeedUrl(account));
  }

  /**
   * Insert the given Contact Group to the currently authenticated account's
   * Contact Group list.
   * 
   * @param contactGroup the contact Group to insert
   * @return The inserted contact Group
   * @throws IOException
   */
  public Contact saveNewContact(Contact contactGroup) throws IOException {
    return super.executeInsert(contactGroup, UrlFactory.getDefaultContactGroupsFeedUrl());
  }

  // ========================== Save / PUT ========================

  /**
   * Saves the given existing Contact Group. If you want to override any
   * intermediate changes make sure you change the etag of the contact to '*'.
   * 
   * @param contactGroup the contact group to save
   * @return The inserted Contacts Group
   * @throws IOException
   */
  public ContactGroup saveContactGroup(ContactGroup contactGroup) throws IOException {
    return super.executeUpdate(contactGroup);
  }

  // ========================== Batch ========================

  /**
   * Executes the contact group batch operation list on the currently (default)
   * authenticated account. The batch fields need to be filled for each
   * ContactGroup object.
   * 
   * @param contactGroups the contact group to save
   * @return The inserted Contacts Group
   * @throws IOException
   */
  public ContactGroupsList executeContactGroupBatchOperations(ContactGroupsList contactGroups)
      throws IOException {
    return super.executeBatch(contactGroups, UrlFactory.getDefaultContactGroupsBatchFeedUrl());
  }

  /**
   * Executes the contact group batch operation list on the given account. The
   * batch fields need to be filled for each ContactGroup object.
   * 
   * @param contactGroups the contact group to save
   * @param account the account on which to execute the batch query
   * @return The inserted Contacts Group
   * @throws IOException
   */
  public ContactGroupsList executeContactGroupBatchOperations(ContactGroupsList contactGroups,
      String account) throws IOException {
    return super.executeBatch(contactGroups, UrlFactory.getContactGroupsBatchFeedUrl(account));
  }
}
